package GetPut(
               ToGet(..), ToPut(..),
               Get(..), GetS(..), Put(..), PutS(..), GetPut(..), peekGet,
               mkGetPut, fifoToGet, fifoToPut, fifoToGetS,
               mkGPFIFO, mkGPFIFO1, mkGPSizedFIFO
              ) where

import FIFO
import FIFOF
import Connectable
import Clocks

--@ \subsubsection{GetPut}
--@ \index{GetPut@\te{GetPut} (package)|textbf}
--@
--@ \te{Get} and \te{Put} are very simple interfaces, consisting of one
--@ method each, \te{get} and \te{put}, respectively.  Although you can
--@ define their behavior,
--@ other Bluespec library components assume that \te{get} retrieves and
--@ consumes one piece of data from an object such as a FIFO, and \te{put}
--@ adds a piece of data.  A rule containing \te{get} will not fire
--@ if the element associated with it is empty.  Similarly, a rule
--@ containing \te{put} will not fire if the element is full.
--@
--@ \te{Get} and \te{Put} are important for two reasons: one is that because
--@ they are so simple, it is almost trivial to connect a
--@ \te{Get} interface to another element's \te{Put}
--@ interface.  The second reason is that other library
--@ components build layers above \te{Get} and \te{Put},
--@ such as \te{mkConnection} and \te{ClientServer}.
--@
--@ The Bluespec library offers a FIFO with \te{Get} and \te{Put} interfaces
--@ instead of the usual enq/deq interfaces.  It also offers
--@ modules to create a \te{Get} or \te{Put} interface for an existing FIFO.
--@
--@ The module \te{mkConnection}, implemented in the Bluespec library,
--@ connects the \te{Get} interface of one component to the
--@ \te{Put} interface of another, as long as the data types of the
--@ elements are the same.  The data transfers occur
--@ whenever the upstream element is not empty and the downstream element is
--@ not full.
--@
--@ Other library components, such as \te{ClientServer} and \te{CompletionBuffer},
--@ are built on
--@ top of \te{Get} and \te{Put} to easily connect common
--@ interconnect topologies.
--@
--@ Often, you will have a module that sends output data
--@ through a FIFO, and another module that accepts input
--@ data through another module.  You can then connect the
--@ two FIFOs together
--@ by instantiating \te{mkConnection (getfifo1, putfifo2)} (for example).
--@
--@ You can write your own \te{get} and \te{put} methods to add
--@ \te{Get} and \te{Put} interfaces to other data types.  If
--@ you do, you need make \te{get} and ActionValue method and
--@ \te{put} an Action method.  \te{get} should not be enabled unless
--@ there is data available and \te{put} should not be enabled
--@ unless there is available space.  These conditions are
--@ implicit in FIFOs.
--@
--@
--@ \index{Get@\te{Get} (interface)|textbf}
--@ The \te{Get} interface declares that the module must have a method
--@ defined called \te{get}.
--@ (Normally, you would not add a \te{Get} interface yourself; instead
--@ you would use a library component that already has a \te{Get} interface
--@ defined, such as mkGPFIFO)
--@
--@ This is how \te{Get} is defined in the library:
--@ % (hence an \te{ActionValue}).

--@ \begin{libverbatim}
--@ interface Get #(type a);
--@    method ActionValue#(a) get();
--@ endinterface: Get
--@ \end{libverbatim}
--@
--@ Here is an example of how to add your own \te{Get} interface:
--@ \begin{libverbatim}
--@ module mkMyFifoUpstream (Get#(int));
--@ ...
--@    method ActionValue#(int) get();
--@        f.deq;
--@        return f.first;
--@    endmethod
--@ \end{libverbatim}
interface Get a =
    get :: ActionValue a

interface GetS a =
    first :: a
    deq  :: Action

--@ \index{Put@\te{Put} (interface)|textbf}
--@ The \te{Put} interface declares that the module must have a method
--@ defined called \te{put}.
--@ (Normally, you would not add a \te{Put} interface yourself; instead
--@ you would use a library component that already has a \te{Put} interface
--@ defined.)
--@ \begin{libverbatim}
--@ interface Put #(type a);
--@     method Action put(a x1);
--@ endinterface: Put
--@ \end{libverbatim}
--@
--@ Here is an example of how to add your own \te{Put} interface:
--@ \begin{libverbatim}
--@ module mkMyFifoDownstream (Put#(int));
--@ ...
--@    method Action put(int x);
--@        f.enq <- x;
--@    endmethod
--@ \end{libverbatim}
interface Put a =
    put :: a -> Action {-# prefix="", result = "UNUSED", arg_names = [put] #-}

interface PutS a =
    offer :: a -> Action
    accepted :: Bool

--@ This is how the library defines the interface \te{GetPut} which associates
--@ \te{Get} \emph{and} \te{Put} interfaces to a type:
--@ \begin{libverbatim}
--@ typedef Tuple2 #(Get#(a), Put#(a)) GetPut #(type a);
--@ \end{libverbatim}
type GetPut a = (Get a, Put a)

--@ You can use \te{peekGet} to peek at an item without getting it.
--@ \index{peekGet@\te{peekGet} (function)|textbf}
--@ \begin{libverbatim}
--@ function a peekGet(Get#(a) g);
--@ \end{libverbatim}
--@ Note: \te{peekGet} will not dequeue an item even if you explicitly
--@ write a \te{get} method with an dequeue action.  Bluespec will not allow
--@ the action to fire.
--@
--@ For example:
--@ \begin{libverbatim}
--@ myReg <= peekGet (myGetFIFO);
--@ \end{libverbatim}
peekGet :: Get a -> a
peekGet g = __value g.get

--@ Given a FIFO you can obtain a \te{Get} interface.  You would do this if
--@ you want a FIFO with only a \te{Get} interface and not a \te{Put}
--@ interface.  If you wanted both, you could simple instance \te{mkGPFIFO}
--@ (see below).
--@ \index{fifoToGet@\te{fifoToGet} (function)|textbf}
--@ The library defines \te{fifoToGet} as such:
--@ \begin{libverbatim}
--@ function Get#(a) fifoToGet(FIFO#(a) f);
--@ \end{libverbatim}
--@ \te{fifoToGet} returns an interface.  Use that interface to access the FIFO using \te{Get}.
--@ For example:
--@ \begin{libverbatim}
--@ Get #(int) myGetFIFO = fifoToGet (myOriginalFIFO);
--@ // or: let myGetFIFO = fifoToGet (myOriginalFIFO);
--@ ...
--@ myReg <= myGetFIFO.get; // Access myOriginalFIFO through the myGetFIFO interface
--@ \end{libverbatim}
fifoToGet :: FIFO a -> Get a
fifoToGet f =
    interface Get
        get = do
                f.deq
                return f.first

fifoToGetS :: FIFO a -> GetS a
fifoToGetS f =
    interface GetS
        first = f.first
        deq   = f.deq

--@
--@ Given a FIFO you can obtain a \te{Put} interface.   You would do this if
--@ you want a FIFO with only a \te{Put} interface and not a \te{Get}
--@ interface.  If you wanted both, you could simple instance \te{mkGPFIFO}
--@ (see below).
--@ \index{fifoToPut@\te{fifoToPut} (function)|textbf}
--@ The library defines \te{fifoToPut} as such:
--@ \begin{libverbatim}
--@ function Put#(a) fifoToPut(FIFO#(a) f);
--@ \end{libverbatim}
--@ \te{fifoToPut} returns an interface.  Use that interface to access the FIFO using \te{Put}.
--@ For example:
--@ \begin{libverbatim}
--@ Put #(int) myPutFIFO = fifoToPut (myOriginalFIFO);
--@ // or: let myPutFIFO = fifoToPut (myOriginalFIFO);
--@ ...
--@ myPutFIFO.put(myReg); // Access myOriginalFIFO through the myPutFIFO interface
--@ \end{libverbatim}
--@
--@ This is an example of how you might write a protocol monitor that watches bus traffic between
--@ a bus and a bus target device:
--@ \begin{libverbatim}
--@ import GetPut::*;
--@ import FIFO::*;
--@
--@ // Watch bus traffic between a bus and a bus target
--@ interface ProtocolMonitorIfc;
--@    // These subinterfaces are defined inside the module
--@    interface Put#(Bus_to_Target_Request)  bus_to_targ_req_ifc;
--@    interface Put#(Target_to_Bus_Response) targ_to_bus_resp_ifc;
--@ endinterface
--@ ...
--@ module mkProtocolMonitor (ProtocolMonitorIfc);
--@    // Input FIFOs that will have Put interfaces added a few lines down
--@    FIFO #(Bus_to_Target_Request) bus_to_targ_reqs <- mkFIFO;
--@    FIFO #(Target_To_Bus_Response) targ_to_bus_resps <- mkFIFO;
--@ ...
--@    // Define the subinterfaces: attach Put interfaces to the FIFOs, and
--@    //   then make those the module interfaces
--@    interface bus_to_targ_req_ifc = fifoToPut (bus_to_targ_reqs);
--@    interface targ_to_bus_resp_ifc = fifoToPut (targ_to_bus_resps);
--@ end module: mkProtocolMonitor
--@
--@ // Top-level module: connect mkProtocolMonitor to the system:
--@ module mkSys (Empty);
--@    ProtocolMonitorIfc pmon <- mkProtocolInterface;
--@ ...
--@    rule pass_bus_req_to_interface;
--@        let x <- bus.bus_ifc.get (x);  // definition not shown
--@        pmon.but_to_targ_ifc.put (x);
--@    endrule
--@ ...
--@ endmodule: mkSys
--@ \end{libverbatim}
fifoToPut :: FIFO a -> Put a
fifoToPut f =
    interface Put
        put x = f.enq x

--@ To create a FIFO and return the two ends of it:
--@ \index{mkGPFIFO@\te{mkGPFIFO} (function)|textbf}
-- @ \begin{libverbatim}
-- @ module mkGetPut(GetPut#(a))
-- @   provisos (Bits#(a, sa));
-- @ \end{libverbatim}
-- @
mkGetPut :: (IsModule m c, Bits a sa) => m (GetPut a)
mkGetPut =
  module
    f :: FIFO a <- mkFIFO
    interface (fifoToGet f, fifoToPut f)

-- @ or
--@ \begin{libverbatim}
--@ module mkGPFIFO(GetPut#(a))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
-- @ (mkGetPut and mkGPFIFO are identical.)
--@
--@ Example:
--@ \begin{libverbatim}
--@ import GetPut::*;
--@
--@ // example structure for the FIFO contents:
--@ typedef enum { Ok, Err } Status_t
--@    deriving (Eq, Bits);
--@ typedef struct {
--@    Status_t  status;
--@    int       info;
--@ } StatusInfo
--@    deriving (Bits);
--@ ...
--@ module mkMyModule (MyInterface);
--@    GetPut #(StatusInfo) aFifoOfStatusInfoStructures <- mkGPFIFO;
--@ ...
--@ endmodule: mkMyModule
--@ \end{libverbatim}
mkGPFIFO :: (IsModule m c, Bits a sa) => m (GetPut a)
mkGPFIFO =
  module
    f :: FIFO a <- mkFIFO
    interface (fifoToGet f, fifoToPut f)

--@ Create a one-entry FIFO and return the two ends of it.
--@ \index{mkGPFIFO1@\te{mkGPFIFO1} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkGPFIFO1(GetPut#(a))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkGPFIFO1 :: (IsModule m c, Bits a sa) => m (GetPut a)
mkGPFIFO1 =
  module
    f :: FIFO a <- mkFIFO1
    interface (fifoToGet f, fifoToPut f)

--@ Create a sized FIFO and return the two ends of it.
--@ \index{mkGPSizedFIFO@\te{mkGPSizedFIFO} (function)|textbf}
--@ \begin{libverbatim}
--@ module mkGPSizedFIFO#(Integer sz)(GetPut#(a))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkGPSizedFIFO :: (IsModule m c, Bits a sa) => Integer -> m (GetPut a)
mkGPSizedFIFO sz =
  module
    f :: FIFO a <- mkSizedFIFO sz
    interface (fifoToGet f, fifoToPut f)

--@ You can easily connect together two modules that have a \te{Get} and
--@ a \te{Put} interface of the same type using \te{mkConnection}.
--@ \te{mkConnection} is a module declared in the \te{ClientServer}
--@ library and defined in this library (or, you can define your own).
--@ A \te{Get} and a \te{Put} interface can be connected either way.
--@ (See the section on \te{Connectable}, below).
 --@ \begin{libverbatim}
 --@ instance Connectable #(Get#(a), Put#(a));
 --@ \end{libverbatim}
--@ Example:
--@ \begin{libverbatim}
--@ module mkSys (Empty);
--@    // Instantiate subsystems
--@    Bus_Ifc             bus       <- mkBus;
--@    Target_Ifc          targ      <- mkTarget;
--@    Initiator_Ifc       initor    <- mkInitiator;
--@
--@    // Connect bus and targ ("to_targ" is a Get i/f, targ is a Put i/f)
--@    Empty x <- mkConnection (bus.to_targ, targ);
--@    // Connect bus and initiator ("to_initor" is a Out i/f, initor is a Get i/f)
--@    Empty y <- mkConnection (bus.to_initor, initor);
--@ ...
--@ endmodule: mkSys
 --@
--@ \end{libverbatim}
instance Connectable (Get a) (Put a)
  where
    mkConnection :: (IsModule m c) => Get a -> Put a -> m Empty
    mkConnection g p =
      module
        rules
         "mkConnectionGetPut":
            when True
             ==> action
                    x :: a <- g.get
                    p.put x

 --@ \lineup
 --@ Note that \te{Connectable} can be used in either direction:
 --@ \begin{libverbatim}
 --@ instance Connectable #(Put#(a), Get#(a));
 --@ \end{libverbatim}
instance Connectable (Put a) (Get a)
  where
    mkConnection :: (IsModule m c) => Put a -> Get a -> m Empty
    mkConnection p g = mkConnection g p

instance Connectable (GetS a) (Put a)
  where
    mkConnection :: (IsModule m c) => GetS a -> Put a -> m Empty
    mkConnection g p =
      module
        rules
         "mkConnectionGetSPut":
            when True
             ==> action
                    let x = g.first
                    g.deq
                    p.put x

instance Connectable (GetS a) (PutS a)
  where
    mkConnection :: (IsModule m c) => GetS a -> PutS a -> m Empty
    mkConnection g p =
      module
        rules
         "mkConnectionGetSPutS_offer":
            when True
             ==> action
                    let x = g.first
                    p.offer x
         "mkConnectionGetSPutS_accept":
            when (p.accepted)
             ==> action
                    g.deq

 --@ \lineup
 --@ Note that \te{Connectable} can be used in either direction:
 --@ \begin{libverbatim}
 --@ instance Connectable #(Put#(a), GetS#(a));
 --@ \end{libverbatim}
instance Connectable (Put a) (GetS a)
  where
    mkConnection :: (IsModule m c) => Put a -> GetS a -> m Empty
    mkConnection p g = mkConnection g p

instance Connectable (PutS a) (GetS a)
  where
    mkConnection :: (IsModule m c) => PutS a -> GetS a -> m Empty
    mkConnection p g = mkConnection g p

instance (Bits a sa) => ClockConv (Get a) where
    mkConverter :: (Prelude.IsModule m c) => Integer -> Get a -> m (Get a)
    mkConverter d getuses =
        module
          ff :: SyncFIFOIfc a <- mkSyncFIFOToCC d (clockOf getuses) (resetOf getuses);
          rules
            "enqueue": when True   ==> action { v <- getuses.get; ff.enq v; }
          interface
            get =  do
                    let v =  ff.first
                    ff.deq
                    return v

instance (Bits a sa) => ClockConv (Put a) where
    mkConverter :: (Prelude.IsModule m c) => Integer -> Put a -> m (Put a)
    mkConverter d putuses =
        module
          ff :: SyncFIFOIfc a <- mkSyncFIFOFromCC d (clockOf putuses)
          rules
             "dequeue":  when True  ==> action { putuses.put ff.first; ff.deq; }
          interface
            put v =  action { ff.enq v; }

--  Classes ToGet and ToPut
class ToGet a b | a -> b where
    toGet :: a -> Get b

class ToPut a b | a -> b where
    toPut :: a -> Put b

instance ToGet (Get a) a where
    toGet g = g

instance ToPut (Put a) a where
    toPut p = p

-- for the FIFO interface
instance ToGet (FIFO a) a where
    toGet f =
        interface Get
            get = do
                  f.deq
                  return f.first


instance ToPut (FIFO a) a where
    toPut f =
        interface Put
            put x = f.enq x

-- For the FIFOF interface
instance ToGet (FIFOF a) a where
    toGet f =
        interface Get
            get = do
                  f.deq
                  return f.first


instance ToPut (FIFOF a) a where
    toPut f =
        interface Put
            put x = f.enq x



-- For the SyncFIFO Ifc  defined in Clocks.bsv
instance ToGet (SyncFIFOIfc a) a where
    toGet f =
        interface Get
            get = do
                  f.deq
                  return f.first


instance ToPut (SyncFIFOIfc a) a where
    toPut f =
        interface Put
            put x = f.enq x

-- Instances ToPut and ToGet for Register interfaces --
-- Note the are no implicit conditions
instance ToGet (Reg a) a where
    toGet r =
        interface Get
            get = return r._read

instance ToPut (Reg a) a where
    toPut r =
        interface Put
            put x = r._write x


instance ToGet (RWire a) a where
    toGet rw =
        interface Get
            get = return $ validValue rw.wget
                  when isValid rw.wget

instance ToPut (RWire a) a where
    toPut rw =
        interface Put
            put x = rw.wset x


instance ToGet (ReadOnly a) a where
    toGet r =
        interface Get
            get = return r._read

instance ToGet (a) a where
    toGet v =
        interface Get
            get = return v

instance ToGet (ActionValue a) a where
    toGet v =
        interface Get
            get = v

instance ToPut (a -> Action) a where
    toPut f =
        interface Put
            put = f
